﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using InvestmentIntelligence.DbModel;
using InvestmentIntelligence.DbModel.Models;

namespace InvestmentIntelligence.Data.Repository.EFImplementation
{
    public class SecurityFinderRepository : ISecurityFinderRepository
    {
        #region Private Fields

        private const string SecurityViewQuery = @"SELECT * FROM [MarketData].[SecurityView]";
        private const string SecuritySearchQuery = @"SELECT TOP {0} * 
FROM [MarketData].[SecurityView] 
WHERE 
SecurityView.ParentId is null 
AND {1}
ORDER BY CHARINDEX('{2}', Name)";


        private readonly string[] FindQueryColumns = new []{"Ric","Ticker","Name"};
        private readonly HashSet<SecurityView> _resultsHash = new HashSet<SecurityView>(SecurityViewComparer.Instance);
        private readonly Regex _regexpFilter;

        #endregion

        #region Public Methods

        public List<SecurityView> FindByName(string term, int limit = 10)
        {

            var filter = GetSingleFieldFilter("Name", GetFilterItemsStartWith(term));
            var query = string.Format(SecuritySearchQuery, limit, filter, term);
            return InternalQuery(filter, query);
          
        }

      
        public SecurityFinderRepository()
        {
            _regexpFilter = new Regex(@"[\w]+");
        }

        private static readonly List<SecurityView> EmptySecurities = new List<SecurityView>(); 
    
        public List<SecurityView> Find(string term, int limit)
        {
            if (string.IsNullOrWhiteSpace(term))
            {
                return EmptySecurities;
            }

            var tasks = new Task[FindQueryColumns.Length];
            int i = 0;
            var filter = string.Join(" AND ", term.Split(new[] {' '}, StringSplitOptions.RemoveEmptyEntries));
            
            foreach (var queryColumn in FindQueryColumns)
            {
                string column = queryColumn;
                var task = Task<List<SecurityView>>.Factory.StartNew(() =>
                {
                    var filterString = string.Format(@"(CONTAINS(({0}), '{1}'))", column, filter);
                    var queryString = string.Format(SecuritySearchQuery, limit, filterString);
                    return InternalQuery(filterString, queryString);
                });

                tasks[i++] = task;
            }
            
            _resultsHash.Clear();
            Task.WaitAll(tasks);

            // merge results
            i = 0;
            int tasksLength = tasks.Length;
            while (_resultsHash.Count < limit && i < tasksLength)
            {
                var task = (Task<List<SecurityView>>) tasks[i];
                _resultsHash.UnionWith(task.Result);
                i++;
            }

            // can return (limit + x) values where x <= limit
            var result = _resultsHash.ToList();
            return result;
        }

        private static List<SecurityView> Query(string query)
        {
            using (var dc = new IIPEntities())
            {
                return dc.Database.SqlQuery<SecurityView>(query).ToList();
            }
        }

        public List<SecurityView> FindByBook(int fundBookId, string term, int limit)
        {

            var query = @"SELECT TOP {0} * 
FROM [MarketData].[SecurityView] 
JOIN [TradingData].[BookSecurityToBenchmark] 
    ON BookSecurityToBenchmark.SecurityId = SecurityView.Id
WHERE 
    SecurityView.ParentId is null
    AND BookSecurityToBenchmark.FundBookEntityId = {1}
    AND {2}
ORDER BY Name ASC";

            var filter = !String.IsNullOrWhiteSpace(term) ? GetSingleFieldFilter("SecurityView.Ric, SecurityView.Ticker, SecurityView.Name", GetFilterItems(term)) : "1=1";
            query = string.Format(query, limit, fundBookId, filter);
            return InternalQuery(filter, query);
        }

        public List<SecurityView> FindByRic(string term)
        {
            return InternalFind(term, "Ric");
        }

        public List<SecurityView> FindByTicker(string term)
        {
            return InternalFind(term, "Ticker");
        }

        #endregion

        #region Private Methods

        private List<SecurityView> InternalQuery(string filter, string query)
        {
            if (string.IsNullOrWhiteSpace(filter))
            {
                return EmptySecurities;
            }

            return Query(query);
        }

        private List<SecurityView> InternalFind(string term, string p)
        {
            var filter = GetSingleFieldFilter(p, GetFilterItems(term));
            var query = string.Format("{0} WHERE {1}", SecurityViewQuery, filter);
            return InternalQuery(filter, query);
        }

      

        private string GetSingleFieldFilter(string fieldName, IEnumerable<string> filterItems )
        {
            var filter = string.Join(" AND ", filterItems);
            filter = string.IsNullOrEmpty(filter) ? string.Empty : string.Format(@"(CONTAINS(({0}), '{1}'))", fieldName, filter);
            return filter;
        }

        private IEnumerable<string> GetFilterItems(string term)
        {
            return from Match match in _regexpFilter.Matches(term) select match.Value;
        }

        private IEnumerable<string> GetFilterItemsStartWith(string term)
        {
            return from Match match in _regexpFilter.Matches(term) select string.Format("\"{0}*\"", match.Value);
        }

        #endregion
    }
}